/*
 * @ : Copyright (c) 2021 Phytium Information Technology, Inc. 
 *  
 * SPDX-License-Identifier: Apache-2.0.
 * 
 * @Date: 2021-07-26 16:27:59
 * @LastEditTime: 2021-09-06 14:56:15
 * @Description:  This files is for intrrupt function of i2c ctrl
 * 
 * @Modify History: 
 *  Ver   Who        Date         Changes
 * ----- ------     --------    --------------------------------------
 * 1.0   Zhugengyu   2021/8/3   init
 */
#include "parameters.h"
#include "ft_assert.h"
#include "dw_i2c.h"
#include "dw_i2c_hw.h"
// #include "gicv3.h"
#include "interrupt.h"

/**
 * @name: I2cClearIntrBits
 * @msg: read and clear intrrupt status bits
 * @return {*}
 * @param {I2cCtrl} *pCtrl, ctrl block of i2c
 */
static u32 I2cClearIntrBits(I2cCtrl *pCtrl)
{
    FT_ASSERTZERONUM(pCtrl);

    const u32 stat = I2C_READ_INTR_STAT(pCtrl);

    if (stat & I2C_INTR_TX_ABRT)
    {
        pCtrl->lastErr = I2C_READ_REG32(pCtrl, I2C_TX_ABRT_SOURCE_OFFSET); /* read out abort sources */
        I2C_READ_REG32(pCtrl, I2C_CLR_TX_ABRT_OFFSET);
    }
        
    if (stat & I2C_INTR_RX_UNDER)
        I2C_READ_REG32(pCtrl, I2C_CLR_RX_UNDER_OFFSET);

    if (stat & I2C_INTR_RX_OVER)
        I2C_READ_REG32(pCtrl, I2C_CLR_RX_OVER_OFFSET);

    if (stat & I2C_INTR_TX_OVER)
        I2C_READ_REG32(pCtrl, I2C_CLR_TX_OVER_OFFSET);

    if (stat & I2C_INTR_RX_DONE)
        I2C_READ_REG32(pCtrl, I2C_CLR_RX_DONE_OFFSET);

    if (stat & I2C_INTR_ACTIVITY)
        I2C_READ_REG32(pCtrl, I2C_CLR_ACTIVITY_OFFSET);

    if (stat & I2C_INTR_STOP_DET)
        I2C_READ_REG32(pCtrl, I2C_CLR_STOP_DET_OFFSET);

    if (stat & I2C_INTR_START_DET)
        I2C_READ_REG32(pCtrl, I2C_CLR_START_DET_OFFSET);

    if (stat & I2C_INTR_GEN_CALL)
        I2C_READ_REG32(pCtrl, I2C_CLR_GEN_CALL_OFFSET);

    return stat;
}

/**
 * @name: I2cDefaultEvtCb
 * @msg: default callback function of events
 * @return {*}
 * @param {void} *pPara, parameters, pass ctrl block pointer here
 */
void I2cDefaultEvtCb(void *pPara)
{
    FT_ASSERTVOID(pPara);
    I2cCtrl *pCtrl = (I2cCtrl *)pPara;
    const u32 status = I2C_READ_INTR_STAT(pCtrl);

    I2C_INFO("i2c id: 0x%x, intr cause: 0x%x", pCtrl->config.instanceId, status);
}


/**
 * @name: I2cRegisterEvtCallback
 * @msg: regist events callback function
 * @return {*}
 * @param {I2cCtrl} *pCtrl, i2c ctrl block
 * @param {I2cIntrEvent} evt, event 
 * @param {I2cEvtHandler} handler, event callback function ptr
 */
void I2cRegisterEvtCallback(I2cCtrl *pCtrl, I2cIntrEvent evt, I2cEvtHandler handler)
{
    FT_ASSERTVOID(pCtrl && evt < MAX_I2C_INTR_EVT);
    pCtrl->evtHandlers[evt] = handler;
}

/**
 * @name: I2cSlaveIntrHandler
 * @msg: handle intrrupt for i2c ctrl in slave mode, refer to i2c_designware_slave.c of linux 4.9
 * @return {*}
 * @param {I2cCtrl} *pCtrl, i2c ctrl block
 */
static void I2cSlaveIntrHandler(I2cCtrl *pCtrl)
{
    FT_ASSERTVOID(pCtrl);
    I2cEvtHandler evtHandler;
    boolean slaveActive = (I2C_STATUS(pCtrl) & I2C_STATUS_SLV_ACTIVITY) ? TRUE : FALSE;
    u32 rawIntrStat = I2C_READ_RAW_INTR_STAT(pCtrl);
    u32 stat = I2cClearIntrBits(pCtrl);
    u32 enable = I2C_READ_REG32(pCtrl, I2C_ENABLE_OFFSET);    

    if (!(enable & I2C_IC_ENABLE) || !(rawIntrStat & ~I2C_INTR_ACTIVITY) || 
         (I2C_SLAVE != pCtrl->config.workMode))
    {
        return;
    }

    /* when receive stop singal from master */
    if (rawIntrStat & I2C_INTR_STOP_DET)
    {
        I2C_CALL_EVT_HANDLER(I2C_EVT_SLAVE_WRITE_REQUESTED, pCtrl)
    }

    /* when receive RD Requst singal from master */
    if ((stat & I2C_INTR_RD_REQ) && slaveActive )
    {
        if (stat & I2C_INTR_RX_FULL)
        {
            pCtrl->curTrans = (I2C_DATA_MASK & I2C_READ_DATCMD(pCtrl));
            I2C_CALL_EVT_HANDLER(I2C_EVT_SLAVE_WRITE_RECEIVED, pCtrl)
            I2C_READ_REG32(pCtrl, I2C_CLR_RD_REQ_OFFSET);
        }
        else
        {
            I2C_READ_REG32(pCtrl, I2C_CLR_RD_REQ_OFFSET);
            I2C_READ_REG32(pCtrl, I2C_CLR_RX_UNDER_OFFSET);
        }

        I2C_CALL_EVT_HANDLER(I2C_EVT_SLAVE_READ_REQUESTED, pCtrl)
        I2C_WRITE_DATCMD(pCtrl, pCtrl->curTrans);       
    }

    if (stat & I2C_INTR_RX_DONE)
    {
        I2C_CALL_EVT_HANDLER(I2C_EVT_SLAVE_READ_PROCESSED, pCtrl)
        I2C_READ_REG32(pCtrl, I2C_CLR_RX_DONE_OFFSET);
        I2C_CALL_EVT_HANDLER(I2C_EVT_SLAVE_STOP, pCtrl)
        return;
    }

    if (stat & I2C_INTR_RX_FULL)
    {
        pCtrl->curTrans = (I2C_DATA_MASK & I2C_READ_DATCMD(pCtrl));
        I2C_CALL_EVT_HANDLER(I2C_EVT_SLAVE_WRITE_RECEIVED, pCtrl)
    }
    else
    {
        I2C_CALL_EVT_HANDLER(I2C_EVT_SLAVE_STOP, pCtrl)
    }

    return;
}

/**
 * @name: I2cMasterIntrHandler
 * @msg: handle intrrupt for i2c ctrl in master mode, 
 *       refer to i2c_designware_master.c of linux 4.9
 * @return {*}
 * @param {I2cCtrl} *pCtrl, i2c ctrl block
 */
static void I2cMasterIntrHandler(I2cCtrl *pCtrl)
{
    FT_ASSERTVOID(pCtrl);
    I2cEvtHandler evtHandler;
    u32 rawIntrStat = I2C_READ_RAW_INTR_STAT(pCtrl);
    u32 stat = I2cClearIntrBits(pCtrl);
    u32 enable = I2C_READ_REG32(pCtrl, I2C_ENABLE_OFFSET);    

    if (!(enable & I2C_IC_ENABLE) || !(rawIntrStat & ~I2C_INTR_ACTIVITY) || 
         (I2C_MASTER != pCtrl->config.workMode))
    {
        return;
    }

    if (stat & I2C_INTR_TX_ABRT) /* trans abort error */
    {
        pCtrl->lastErr = 0;
        pCtrl->lastErr |= I2C_READ_REG32(pCtrl, I2C_TX_ABRT_SOURCE_OFFSET);
        I2C_CALL_EVT_HANDLER(I2C_EVT_MASTER_TRANS_ABORTED, pCtrl)
        I2cSetIntrruptMask(pCtrl, I2C_INTR_ALL_MASK, FALSE); /* disable all intr */
        return;
    }

    if (stat & I2C_INTR_RX_FULL) /* rx complete */
    {
        I2C_CALL_EVT_HANDLER(I2C_EVT_MASTER_READ_DONE, pCtrl);
    }

    if (stat & I2C_INTR_TX_EMPTY) /* tx complete */
    {
        I2C_CALL_EVT_HANDLER(I2C_EVT_MASTER_WRITE_DONE, pCtrl);
    }

    return;
}

/**
 * @name: I2cIntrHandler
 * @msg: intrrput event entry for i2c ctrl in slave & master mode
 * @return {*}
 * @param {s32} vector, parameters from system intr handler
 * @param {void} *param
 */
static void I2cIntrHandler(s32 vector, void *param)
{
    FT_ASSERTVOID(param);
    I2cCtrl *pCtrl = (I2cCtrl *)param;
    I2cEvtHandler evtHandler;

    if (I2C_MASTER == pCtrl->config.workMode)
    {
        I2cMasterIntrHandler(pCtrl);
    }
    else if (I2C_SLAVE == pCtrl->config.workMode)
    {
        I2cSlaveIntrHandler(pCtrl);
    }

    return;
}

/**
 * @name: I2cEnableIntr
 * @msg: enable and setup intrrupt for slave & master i2c
 * @return {*}
 * @param {I2cCtrl} *pCtrl
 */
u32 I2cEnableIntr(I2cCtrl *pCtrl)
{
    FT_ASSERTZERONUM(pCtrl);

    I2cConfig *pConfig = &pCtrl->config;

    /* disable all i2c irq */
    I2C_CLEAR_ALL_IRQ(pCtrl);

    /* umask i2c irq */
    InterruptSetPriority(pConfig->irqNum, pConfig->irqPrority);
    InterruptInstall(pConfig->irqNum, I2cIntrHandler, pCtrl, pConfig->instanceName); 

    /* register intr callback */
    if (I2C_MASTER == pConfig->workMode)
    {
        I2cRegisterEvtCallback(pCtrl, 
                              I2C_EVT_MASTER_TRANS_ABORTED,
                              I2cDefaultEvtCb);
        I2cRegisterEvtCallback(pCtrl, 
                              I2C_EVT_MASTER_READ_DONE,
                              I2cDefaultEvtCb);
        I2cRegisterEvtCallback(pCtrl, 
                              I2C_EVT_MASTER_WRITE_DONE,
                              I2cDefaultEvtCb);

        /* by default not enable master intr since there is no performance enhance expected */
        I2cSetIntrruptMask(pCtrl, I2C_INTR_MASTER_DEF_MASK, FALSE); 
    }
    else if (I2C_SLAVE == pConfig->workMode)
    {
        I2cRegisterEvtCallback(pCtrl, 
                              I2C_EVT_SLAVE_READ_REQUESTED,
                              I2cDefaultEvtCb);     
        I2cRegisterEvtCallback(pCtrl, 
                              I2C_EVT_SLAVE_WRITE_REQUESTED,
                              I2cDefaultEvtCb);                                 
        I2cRegisterEvtCallback(pCtrl, 
                              I2C_EVT_SLAVE_READ_PROCESSED,
                              I2cDefaultEvtCb);   
        I2cRegisterEvtCallback(pCtrl, 
                              I2C_EVT_SLAVE_WRITE_RECEIVED,
                              I2cDefaultEvtCb);    
        I2cRegisterEvtCallback(pCtrl, 
                              I2C_EVT_SLAVE_STOP,
                              I2cDefaultEvtCb);

        I2cSetIntrruptMask(pCtrl, I2C_INTR_SLAVE_DEF_MASK, TRUE);
        
        /* enable irq */
        InterruptUmask(pConfig->irqNum);
    }
    else
    {
        FT_ASSERTZERONUM(0);
    }
    
    return I2C_SUCCESS;
}

/**
 * @name: I2cGetIntrruptMask
 * @msg: get intrrupt mask bits
 * @return {*}
 * @param {I2cCtrl} *pCtrl
 */
u32 I2cGetIntrruptMask(I2cCtrl *pCtrl)
{
    FT_ASSERTZERONUM(pCtrl);
    return I2C_READ_INTR_MASK(pCtrl);
}

/**
 * @name: I2cSetIntrruptMask
 * @msg: set intrrupt mask bits, enable or disable
 * @return {*}
 * @param {I2cCtrl} *pCtrl
 * @param {u32} mask, target intr
 * @param {boolean} enable, TRUE: enable, FALSE: disable
 */
void I2cSetIntrruptMask(I2cCtrl *pCtrl, u32 mask, boolean enable)
{
    FT_ASSERTVOID(pCtrl);
    
    /* These bits mask their corresponding interrupt status bits. 
    They are active high; a value of 0 prevents a bit from generating an interrupt. */
    if (TRUE == enable)
    {
        I2C_WRITE_INTR_MASK(pCtrl, (mask | I2cGetIntrruptMask(pCtrl))); /* set 1, enable intr */
    }
    else
    {
        I2C_WRITE_INTR_MASK(pCtrl, ((~mask) & I2cGetIntrruptMask(pCtrl))); /* set 0, disable intr */
    }
}